package com.fucking.great.context.engine;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import groovy.transform.ThreadInterrupt;
import groovy.transform.TimedInterrupt;
import org.codehaus.groovy.ast.stmt.AssertStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer;
import org.codehaus.groovy.control.customizers.SecureASTCustomizer;
import org.codehaus.groovy.syntax.Types;
import org.kohsuke.groovy.sandbox.GroovyInterceptor;
import org.kohsuke.groovy.sandbox.SandboxTransformer;

import java.util.*;

/**
 * @deprecated  这个他会带锁影响性能.
 * java调用groovy工具类（engine方法为执行指定文件）
 * @see GroovyClassLoaderEngine
 * @date 2020-09-14
 */
@Deprecated
public class GroovyEngine extends BaseEngine {

    //
    protected static final GroovyShell GROOVY_SHELL;
    static{
        // 创建SecureASTCustomizer
        final SecureASTCustomizer secure = new SecureASTCustomizer();
        // 禁止使用闭包
        secure.setClosuresAllowed(true);
        List<Integer> tokensBlacklist = new ArrayList<>();
        // 添加关键字黑名单 while和goto
//        tokensBlacklist.add(Types.KEYWORD_WHILE);
        tokensBlacklist.add(Types.KEYWORD_GOTO);
        secure.setTokensBlacklist(tokensBlacklist);
        // 设置直接导入检查
        secure.setIndirectImportCheckEnabled(true);
        // 添加导入黑名单，用户不能导入JSONObject
        List<String> list = new ArrayList<>();
        list.add("com.alibaba.fastjson.JSONObject");
        secure.setImportsBlacklist(list);
        // statement 黑名单，不能使用while循环块
        List<Class<? extends Statement>> statementBlacklist = new ArrayList<>();
//        statementBlacklist.add(WhileStatement.class);  // while循环代码块.
        statementBlacklist.add(AssertStatement.class);
        secure.setStatementsBlacklist(statementBlacklist);

        // 拒绝导入的jar包。
        List<Class> receiversBlackList = new ArrayList<>();
//        receiversBlackList.add(System.class);
        receiversBlackList.add(Runtime.class);
        secure.setReceiversClassesBlackList(receiversBlackList);
        // 自定义CompilerConfiguration，设置AST
        final CompilerConfiguration config = new CompilerConfiguration();
        //
        config.addCompilationCustomizers(secure);


        // 添加线程中断拦截器，可拦截循环体（for,while）、方法和闭包的首指令
        config.addCompilationCustomizers(new ASTTransformationCustomizer(ThreadInterrupt.class));
        new GroovyInterceptor(){
            @Override
            public Object onStaticCall(GroovyInterceptor.Invoker invoker, Class receiver, String method, Object... args) throws Throwable {
                if ( ( receiver == System.class && method.equals("exit") )
                        || ( receiver == Runtime.class)
                ) {
                    throw new SecurityException(String.format("No call Class on [%s]  ,  method [%s]  . please check",receiver.getName() ,method ));
                }
                return super.onStaticCall(invoker, receiver, method, args);
            }
        }.register();
        // 添加线程中断拦截器，可中断超时线程，当前定义超时时间为s
        Map<String, Object> timeoutArgs = new HashMap<>();
        timeoutArgs.put("value", 60*60);   // 默认一个小时超时时间。
        config.addCompilationCustomizers(new ASTTransformationCustomizer(timeoutArgs, TimedInterrupt.class));

        // 沙盒环境
        config.addCompilationCustomizers(new SandboxTransformer());
        //
        GROOVY_SHELL = new GroovyShell(config);
    }

    @Override
    public Object engine(String cacheKey, String scriptStr, Map<String, Object> variable, String... importStr) {
        StringBuilder sb = new StringBuilder();
        if (ArrayUtil.isNotEmpty(importStr)) {
            for (String str : importStr) {
                // import com.alibaba.fastjson.JSONObject;
                sb.append("import ").append(str).append(';');
            }
        }
        sb.append(scriptStr);
        return engine(cacheKey,sb.toString(),variable);
    }

    @Override
    public Object engine(String cacheKey, String scriptStr, Map<String, Object> variable) {
        Binding binding = new Binding();
        if (MapUtil.isNotEmpty(variable)) {
            variable.entrySet().stream().filter(entry -> StrUtil.isNotBlank(entry.getKey()) && ObjectUtil.isNotNull(entry.getValue()))
                    .forEach(entry -> binding.setVariable(entry.getKey(), entry.getValue()));
        }
        Object result;
        Script script = getScriptInstance(cacheKey,scriptStr);
        //对象锁，因为要赋值，所以一个对象同时只能被一个线程调用
        synchronized (script){
            script.setBinding(binding);
            result = script.run();
        }
        return result;
    }


    public   Script getScriptInstance(String cacheKey,String scriptStr) {
       return   GROOVY_SHELL.parse(scriptStr);
    }

}
